/// Rust常见集合

// Rust 标准库中包含一系列被称为 集合（collections）的非常有用的数据结构。
// 大部分其他数据类型都代表一个特定的值，不过集合可以包含多个值。不同于内建的数组和元组类型，
// 这些集合指向的数据是储存在堆上的，这意味着数据的数量不必在编译时就已知，并且还可以随着程序的运行增长或缩小。
// 每种集合都有着不同功能和成本，而根据当前情况选择合适的集合，这是一项应当逐渐掌握的技能。
// 在这一章里，我们将详细的了解三个在 Rust 程序中被广泛使用的集合：
//     vector 允许我们一个挨着一个地储存一系列数量可变的值
//     字符串（string）是字符的集合。我们之前见过 String 类型，不过在本章我们将深入了解。
//     哈希 map（hash map）允许我们将值与一个特定的键（key）相关联。这是一个叫做 map 的更通用的数据结构的特定实现。


/// vector 用来储存一系列的值
// 第一个类型是 Vec<T>，也被称为 vector。vector 允许我们在一个单独的数据结构中储存多个值，
// 所有值在内存中彼此相邻排列。vector 只能储存相同类型的值。
/// 新建 vector
// 注意这里我们增加了一个类型标注。因为没有向这个 vector 中插入任何值，Rust 并不知道我们想要储存什么类型的元素。
// 这一点非常重要。vector 是用泛型实现的，现在，我们知道 Vec 是一个由标准库提供的类型，它可以存放任何类型，
// 而当 Vec 存放某个特定类型时，这个 Vec 的类型就自动确定了。当然我们也可以提前如下指定类型约束
fn vector_1() {
    let v: Vec<i32> = Vec::new();
}
// 在更实际的代码中，一旦插入值 Rust 就可以推断出想要存放的类型，所以你很少会需要这些类型标注。
// 更常见的做法是使用初始值来创建一个 Vec，而且为了方便 Rust 提供了 vec! 宏。
// 这个宏会根据我们提供的值来创建一个新的 Vec。
fn vector_2() {
    let v = vec![1, 2, 3];
}
/// 操作 Vec 集合
fn main() {
    let mut v = Vec::new();

    // 更新 vector
    v.push(5);
    v.push(6);

    // 移除并返回 vector 的最后一个元素
    let x = v.pop();

    // 读取 vector 的元素
    // 访问 vector 中一个值的两种方式，索引语法或者 get 方法
    let third: &i32 = &v[2];    // 索引访问
    println!("The third element is {}", third);

    match v.get(2) {    // 使用 get 方法访问
        Some(third) => println!("The third element is {}", third),
        None => println!("There is no third element."),
    }

    // 遍历 vector 中的元素
    for i in &v {
        println!("{}", i);
    }

    for i in &mut v {
        *i += 50;
    }
}


// 使用枚举来储存多种类型
// 我们提到 vector 只能储存相同类型的值。这是很不方便的；绝对会有需要储存一系列不同类型的值的用例。
// 幸运的是，枚举的成员都被定义为相同的枚举类型，所以当需要在 vector 中储存不同类型值时，
// 我们可以定义并使用一个枚举！
fn main() {
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}


/// 使用字符串存储 UTF-8 编码的文本
// Rust 的核心语言中只有一种字符串类型：str，字符串 slice，它通常以被借用的形式出现，&str
fn main() {
    // String 是一个 Vec<u8> 的封装。
    let mut s = String::new();

    let data = "initial contents";
    let s = String::from("initial contents");
    // 将字符串切片 &str 变为字符串 String
    let s = data.to_string();
    // 该方法也可直接用于字符串字面量
    let s = "initial contents".to_string();
    // 将字符串 String 变为字符串切片 &str
    let x = s.as_str();

    // 更新字符串
    // 使用 push_str 和 push 附加字符串
    s.push_str("bar");
    // 向字符串尾插单个字符
    s.push('x');
    // 将字符串传化成（字符）字节数组
    let x1 = s.as_bytes();

    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");
    // format! 与 println! 的工作原理相同，不过不同于将输出打印到屏幕上，它返回一个带有结果内容的 String
    let s = format!("{}-{}-{}", s1, s2, s3);

    // 字符串遍历
    for c in data.chars() {
        println!("{}", c);
    }
}


/// 哈希 map 储存键值对
fn main() {
    use std::collections::HashMap;

    // 新建一个哈希 map
    let mut scores = HashMap::new();
    // 增加元素
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    // 获取指定键的值
    if let Some(data) = scores.get("Blue") {
        println!("the value of Blue is: {}", data)
    }
    let score = scores.get("Blue");

    // 覆盖一个值
    // 如果我们插入了一个键值对，接着用相同的键插入一个不同的值，与这个键相关联的旧值将被替换。
    scores.insert(String::from("Blue"), 25);

    // 遍历哈希 map 中的每一个键值对
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }

    // 只在键没有对应值时插入
    // 我们经常会检查某个特定的键是否有值，如果没有就插入一个值。为此哈希 map 有一个特有的 API，
    // 叫做 entry，它获取我们想要检查的键作为参数。如果没有，就插入值。存在时就返回这个值的可变引用
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);

    /// 哈希 map 和所有权
    // 对于像 i32 这样的实现了 Copy trait 的类型，其值可以拷贝进哈希 map。
    // 对于像 String 这样拥有所有权的值，其值将被移动而哈希 map 会成为这些值的所有者，
    let field_name = String::from("Favorite color");
    let field_value = String::from("Blue");

    let mut map = HashMap::new();
    map.insert(field_name, field_value);
    // 这里 field_name 和 field_value 不再有效，
    // 尝试使用它们看看会出现什么编译错误！
}
